package de.uni_hamburg.informatik.mci.lineracalendar.view;

import de.uni_hamburg.informatik.mci.lineracalendar.R;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.media.effect.Effect;
import android.support.v4.view.GestureDetectorCompat;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.EdgeEffectCompat;
import android.util.Log;
import android.util.MonthDisplayHelper;
import android.view.Display;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.widget.EdgeEffect;
import android.widget.Scroller;

public class ViewCalender extends View {

	private final static String TAG = "ViewCalender";
	private final int NUMBER_OF_MONTH = 12;
	private final int NUMBER_OF_HOURS = 24;
	private final float TEXT_SIZE = 12;
	// private Rect mDestRect = new Rect();
	private Rect mYearRect = new Rect();
	private Rect[] mMonthRect = new Rect[NUMBER_OF_MONTH];
	private Rect mDayRect = new Rect();
	private Rect mHoursRect = new Rect();
	private float[] mMonthLinies = new float[NUMBER_OF_MONTH * 4];
	private float[] mDayLinies = new float[31 * 4];
	private float[] mHoursLinies = new float[NUMBER_OF_HOURS * 4];
	private float[] mHeadLinies = new float[NUMBER_OF_MONTH * 4];

	private Paint paintMonth;
	private Paint paintMonth1;
	private Paint bgpaint = new Paint();
	private int mcellHeight = 100;
	private int mCellHeightmin = 100;
	private int mViewWidth;
	private int mViewHeight;
	private static int MIN_SPANY = 10;
	private float mStartingSpanY = 0;
	private ScaleGestureDetector mScaleGestureDetector;
	private GestureDetectorCompat mGestureDetector;
	private Scroller mScroller;
	private final EdgeEffect mEdgeEffectTop;
	private final EdgeEffect mEdgeEffectBottom;
	private Rect mboundRect = new Rect();
	private EdgeEffect mEdgeEffectLeft;
	private EdgeEffect mEdgeEffectRight;
	private boolean mEdgeEffectLeftActive;
	private boolean mEdgeEffectTopActive;
	private boolean mEdgeEffectRightActive;
	private boolean mEdgeEffectBottomActive;
	private RectF mCurrentViewRect = new RectF();
	private Resources resources;
	private static int COLOR_LABEL;
	private static int MONTH_TEXT_SIZE;
	private static int M_LABEL_BACKROUNG;
	private static int M_HORIZONTAL_INNERE_LINIE;
	private static final float INNERE_LINIE_WIDTH = 1;
	private int mMonthTextHeight;
	private static final int MONTH_GAP = 1;
	private DayView dayView;
	private int mCellHeightBeforeScaleGesture;

	private static final float AXIS_X_MIN = -1f;
	private static final float AXIS_X_MAX = 1f;
	private static final float AXIS_Y_MIN = -1f;
	private static final float AXIS_Y_MAX = 1f;

	// /////////////////////////////////////Scroll
	// Varialblen///////////////////////////////////777

	private boolean mStartingScroll = false;
	private float mInitialScrollY;
	private boolean mRecalCenter = false;
	private float mGestureCenter = 0;
	private int mViewStartY;
	private int mScrollStartY;
	private int TOUCH_MODE_INITIAL_STATE = 0;
	private int mTouchMode = TOUCH_MODE_INITIAL_STATE;
	private static final int TOUCH_MODE_VSCROLL = 0x20;
	private static final int TOUCH_MODE_DOWN = 1;
	private int mMaxViewStartY;
	private int mFirstMonth = -1;
	private int mFirstHourOffset;
	private boolean mScrolling = false;
	private static final int SELECTION_HIDDEN = 0;
	private int mSelectionMode = SELECTION_HIDDEN;
	private boolean mOnFlingCalled;

	@SuppressLint("NewApi")
	public ViewCalender(Context context) {
		super(context);
		mScaleGestureDetector = new ScaleGestureDetector(context,
				new ScaleGesture());
		mGestureDetector = new GestureDetectorCompat(context,
				new DetectorGesture());
		// mboundRect.set(getLeft(), getTop(), getHeight(), getBottom()-10);
		mScroller = new Scroller(context);
		mEdgeEffectTop = new EdgeEffect(context);
		mEdgeEffectBottom = new EdgeEffect(context);
		mEdgeEffectRight = new EdgeEffect(context);
		mEdgeEffectLeft = new EdgeEffect(context);
		dayView = new DayView(context);
		resources = context.getResources();

		COLOR_LABEL = resources.getColor(R.color.calender_month_label);
		MONTH_TEXT_SIZE = (int) resources.getDimension(R.dimen.month_text_size);
		M_LABEL_BACKROUNG = resources
				.getColor(R.color.calendar_hour_background);
		M_HORIZONTAL_INNERE_LINIE = resources
				.getColor(R.color.line_inner_horizontal_color);
		paint();

	}

	// @SuppressLint("NewApi")
	// private void releaseEdgeEffects() {
	// mEdgeEffectLeftActive = mEdgeEffectTopActive = mEdgeEffectRightActive =
	// mEdgeEffectBottomActive = false;
	// mEdgeEffectLeft.onRelease();
	// mEdgeEffectTop.onRelease();
	// mEdgeEffectRight.onRelease();
	// mEdgeEffectBottom.onRelease();
	// }

	private void paint() {
		// paintMonth = new Paint();
		// paintMonth.setAntiAlias(true);
		// paintMonth.setColor(Color.GRAY);
		// paintMonth.setTextSize(TEXT_SIZE);

		paintMonth1 = new Paint();
		paintMonth1.setAntiAlias(true);
		paintMonth1.setColor(Color.BLUE);
		paintMonth1.setTextSize(TEXT_SIZE);

	}

	@SuppressLint("NewApi")
	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
		mViewHeight = h;
		mViewWidth = w;
		mEdgeEffectTop.setSize(mViewWidth, mViewHeight);
		mEdgeEffectBottom.setSize(mViewWidth, mViewHeight);
		Paint p = new Paint();
		p.setTextSize(MONTH_TEXT_SIZE);
		mMonthTextHeight = (int) Math.abs(p.ascent());
		mMaxViewStartY = mcellHeight * NUMBER_OF_MONTH;
		// mEdgeEffectTop.setSize(mViewWidth, mViewHeight);
		// mEdgeEffectBottom.setSize(mViewWidth, mViewHeight);
		// mYearRect.set(0, 2, mViewWidth, mViewHeight);
		// mDayRect.set(0, 2, getWidth(), getHeight());
		// mHoursRect.set(0, 2, getWidth(), getHeight());

	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

		int width = MeasureSpec.getSize(widthMeasureSpec);
		int height = 12 * 100;
		setMeasuredDimension(width, height);

	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		mScaleGestureDetector.onTouchEvent(event);
		mGestureDetector.onTouchEvent(event);
		return true;
	}

	@Override
	protected void onDraw(Canvas canvas) {

		super.onDraw(canvas);
		doDraw(canvas);
		// int clipRestoreCount = canvas.save();
		// mYearRect.set(2, 2, mViewWidth, mViewHeight);
		// canvas.clipRect(mYearRect);
		// canvas.restoreToCount(clipRestoreCount);
		// canvas.drawRect(mYearRect, paintMonth);

	}

	private void doDraw(Canvas canvas) {

		Paint p = bgpaint;
		Rect r = mYearRect;
		Rect[] rec = mMonthRect;
		// Rect rect = mYearRect;
		if (mcellHeight < 150) {
			doDrawBgColorLabel(r, canvas, p);
		} else {
			doDrawHeads(rec, canvas, p);
		}
		doDrawMonth(r, canvas, p);

		doDrawLabelOfMonth(r, canvas, p);
		// doDrawDay(mDayRect, canvas, 2, paintMonth1);
		// doDrawHours(mHoursRect, canvas, 2, paintMonth1);

	}

	private void doDrawMonth(Rect rect, Canvas canvas, Paint p) {
		int linieIndex = 0;

		// rect = mYearRect;
		int mYLinie = 0;

		int mArea = calculateHeightCellMonth();
		int alpha = 50;
		p.setColor(M_HORIZONTAL_INNERE_LINIE);
		p.setStrokeWidth(INNERE_LINIE_WIDTH);
		p.setAntiAlias(false);

		for (int month = 0; month < NUMBER_OF_MONTH; month++) {
			mMonthLinies[linieIndex++] = ModuleConstants.M_MARGIN_LEFT;
			mMonthLinies[linieIndex++] = mYLinie;
			mMonthLinies[linieIndex++] = mViewWidth;
			mMonthLinies[linieIndex++] = mYLinie;
			// zeichen die Linie für die Header der Monats nach der Zoomen

			// mMonthLinies[linieIndex++] = ModuleConstants.M_MARGIN_LEFT;
			// mMonthLinies[linieIndex++] = mYLinie + headLinie;
			// mMonthLinies[linieIndex++] = mViewWidth ;
			// mMonthLinies[linieIndex++] = mYLinie + headLinie;

			// if (mArea > 110 && mArea < 150) {
			// // paintMonth1.setAlpha(alpha);
			// // alpha += 50;
			// doDrawDay(mDayRect, canvas, mYLinie, paintMonth1);
			// } else
			// if (mArea >= 150) {
			// paintMonth1.setAlpha(255);
			// doDrawDay(mDayRect, canvas, mYLinie, paintMonth1);
			// }

			// headLinie += mYLinie +50;
			mYLinie += mcellHeight;
		}

		// }
		// if(mcellHeight > 150){
		// }

		canvas.drawLines(mMonthLinies, 0, linieIndex, p);

		// doDrawDay(rect, canvas, paintMonth1);

	}

	private void doDrawDay(Rect rect, Canvas canvas, int distance, Paint p) {

		int mDayIndex = 0;
		int k = 0;
		int mArea = calculateHeightCellDay();
		int alpha = 50;
		int mYLinie;

		for (int day = 1; day <= 31; day++) {
			// mYLinie = k + distance;
			mDayLinies[mDayIndex++] = rect.left;
			mDayLinies[mDayIndex++] = k + distance;
			mDayLinies[mDayIndex++] = rect.right;
			mDayLinies[mDayIndex++] = k + distance;

			// if (mArea > 110 && mArea < 150) {
			// paintMonth1.setAlpha(alpha);
			// alpha += 50;
			// doDrawHours(mHoursRect, canvas, k, paintMonth1);
			// } else if (mArea >= 150) {
			// paintMonth1.setAlpha(255);
			// doDrawHours(mHoursRect, canvas, k, paintMonth1);
			// }
			p.setAlpha(2 * day);
			k += mcellHeight / 31;

		}
		canvas.drawLines(mDayLinies, 0, mDayIndex, p);

	}

	private int getheightofDisplay() {
		Display display = getDisplay();
		Point size = new Point();
		display.getSize(size);
		int height = size.y;
		return height;
	}

	/**
	 * this function is for the draw the the Hours of calender
	 * 
	 * @param rect
	 *            : the hours rectangle
	 * @param canvas
	 *            : canvas
	 * @param distance
	 *            : distance between the cell of day
	 * @param p
	 *            paint
	 */

	private void doDrawHours(Rect rect, Canvas canvas, int distance, Paint p) {

		int mHoursIndex = 0;
		int k = 0;

		for (int day = 0; day < NUMBER_OF_HOURS; day++) {
			mHoursLinies[mHoursIndex++] = rect.left;
			mHoursLinies[mHoursIndex++] = k + distance;// y + (k * day);
			mHoursLinies[mHoursIndex++] = rect.right;
			mHoursLinies[mHoursIndex++] = k + distance;// y + (k * day);
			k += mcellHeight / NUMBER_OF_HOURS;

		}
		canvas.drawLines(mHoursLinies, 0, mHoursIndex, p);

	}

	private void doDrawBgColorLabel(Rect r, Canvas canvas, Paint p) {

		r.left = ModuleConstants.M_MARGIN_LEFT;
		r.right = ModuleConstants.M_MONTH_MARGIN_RIGHT;
		r.bottom = mViewHeight;
		r.top = mYearRect.top;
		p.setColor(M_LABEL_BACKROUNG);
		p.setStyle(Style.FILL);

		p.setAntiAlias(true);

		canvas.drawRect(r, p);
		p.setColor(M_HORIZONTAL_INNERE_LINIE);
		canvas.drawLine(r.right, 0, r.right, r.bottom, p);

	}

	private void doDrawLabelOfMonth(Rect r, Canvas canvas, Paint p) {
		// r.left = ModuleConstants.M_MARGIN_LEFT;
		// r.right = ModuleConstants.M_MONTH_MARGIN_RIGHT;
		// r.bottom = mViewHeight;
		// r.top = mYearRect.top;
		p.setColor(COLOR_LABEL);
		p.setTextSize(MONTH_TEXT_SIZE);
		p.setTypeface(Typeface.DEFAULT);
		p.setTextAlign(Paint.Align.LEFT);
		p.setAntiAlias(true);
		int y = MONTH_GAP + mMonthTextHeight + ModuleConstants.M_MARGIN_LEFT;
		for (int i = 0; i < NUMBER_OF_MONTH; i++) {
			String monthLabel = CalendarData.monthLabel[i];
			canvas.drawText(monthLabel,
					ModuleConstants.MONTH_MARGIN_LABEL_LEFT, y, p);
			y += mcellHeight + MONTH_GAP;
		}

	}

	private void doDrawHeads(Rect[] r, Canvas canvas, Paint p) {
		p.setColor(M_HORIZONTAL_INNERE_LINIE);
		p.setStyle(Style.FILL);
		p.setAntiAlias(true);
		p.setStrokeWidth(INNERE_LINIE_WIDTH);
		int linieindex = 0;
		int y = 40;
		int bottom = 40;
		int top = 0;

		for (int head = 0; head < NUMBER_OF_MONTH; head++) {
			mHeadLinies[linieindex++] = ModuleConstants.M_MARGIN_LEFT;
			mHeadLinies[linieindex++] = y;
			mHeadLinies[linieindex++] = mViewWidth;
			mHeadLinies[linieindex++] = y;
			y += mcellHeight;
		}
		canvas.drawLines(mHeadLinies, 0, linieindex, p);

		// r[head].left = 0;
		// r[head].right = mViewWidth;
		// r[head].bottom = bottom;
		// r[head].top = top;
		// p.setColor(M_LABEL_BACKROUNG);
		// p.setStyle(Style.FILL);
		// canvas.drawRect(r[head], p);
		// bottom += mcellHeight;
		// top += mcellHeight;

		// p.setAntiAlias(true);

	}

	private int calculateHeightCellMonth() {
		return mYearRect.height() / NUMBER_OF_MONTH;
	}

	private int calculateHeightCellDay() {
		return mDayRect.height() / 31;
	}

	private class ScaleGesture extends
			ScaleGestureDetector.SimpleOnScaleGestureListener {

		@SuppressLint("NewApi")
		@Override
		public boolean onScaleBegin(ScaleGestureDetector detector) {

			float gesturePixelInCenter = detector.getFocusY();

			mGestureCenter = (mViewStartY + gesturePixelInCenter) / mcellHeight;
			mStartingSpanY = ScaleGestureDetectorCompat
					.getCurrentSpanY(detector);
			mCellHeightBeforeScaleGesture = mcellHeight;
			return true;
		}

		@Override
		public boolean onScale(ScaleGestureDetector detector) {
			// Log.e(VIEW_LOG_TAG, "test on Scale");
			float spanY = ScaleGestureDetectorCompat.getCurrentSpanY(detector);
			mcellHeight = (int) (mCellHeightBeforeScaleGesture * spanY / mStartingSpanY);
			int gesturePixelInCenter = (int) detector.getFocusY();

			if (mcellHeight < mCellHeightmin) {
				mStartingSpanY = spanY;
				mcellHeight = mCellHeightmin;
				mCellHeightBeforeScaleGesture = mCellHeightmin;
			}
			// mGestureCenter = gesturePixelInCenter / mcellHeight;
			mViewStartY = (int) (mGestureCenter * mcellHeight)
					- gesturePixelInCenter;
			if (mViewStartY < 0) {
				mViewStartY = 0;
				mGestureCenter = (mViewStartY + gesturePixelInCenter)
						/ (float) (mcellHeight);
			} else if (mViewStartY > mMaxViewStartY) {
				mViewStartY = mMaxViewStartY;
				mGestureCenter = (mViewStartY + gesturePixelInCenter)
						/ (float) (mcellHeight);
			}

			computeFirstMonth();
			// mcellHeight *= detector.getScaleFactor();
			invalidate();
			return true;
		}

		@Override
		public void onScaleEnd(ScaleGestureDetector detector) {
			mStartingSpanY = 0;

		}
	}

	private void computeFirstMonth() {
		// Compute the first full hour that is visible on screen
		mFirstMonth = (mViewStartY + mcellHeight + MONTH_GAP - 1)
				/ (mcellHeight + MONTH_GAP);
		mFirstHourOffset = mFirstMonth * (mcellHeight + MONTH_GAP)
				- mViewStartY;
	}

	private class DetectorGesture extends
			GestureDetector.SimpleOnGestureListener {

		@Override
		public boolean onDown(MotionEvent e) {
			Log.e(TAG, "GestureDetector.onDown");
			mTouchMode = TOUCH_MODE_DOWN;
			mOnFlingCalled = false;
			int y = (int) e.getY();
			invalidate();
			return true;
		}

		@SuppressLint("NewApi")
		@Override
		public boolean onScroll(MotionEvent e1, MotionEvent e2,
				float distanceX, float distanceY) {

			Log.e(TAG, "GestureDetector.onScroll");
			if (mStartingScroll) {
				mInitialScrollY = 0;
				mStartingScroll = false;
			}
			mInitialScrollY += distanceY;
			int distance = (int) mInitialScrollY;
			float focusY = getAverageY(e2);
			if (mRecalCenter) {
				mGestureCenter = (mViewStartY + focusY) / mcellHeight;
				mRecalCenter = false;
			}
			if (mTouchMode == TOUCH_MODE_DOWN) {
				int absDistance = Math.abs(distance);
				mScrollStartY = mViewStartY;
			}
			// if ((mTouchMode & TOUCH_MODE_VSCROLL) != 0) {
			mViewStartY = (int) ((mGestureCenter * mcellHeight) - focusY);
			final int pulledToY = (int) (mScrollStartY + distanceY);
			if (pulledToY < 0) {
				mEdgeEffectTop.onPull(distanceY / mViewHeight);
				if (!mEdgeEffectBottom.isFinished()) {
					mEdgeEffectBottom.onRelease();
				}
			} else if (pulledToY > mMaxViewStartY) {
				mEdgeEffectBottom.onPull(distanceY / mViewHeight);
				if (!mEdgeEffectTop.isFinished()) {
					mEdgeEffectTop.onRelease();
				}
			}
			if (mViewStartY < 0) {
				mViewStartY = 0;
				mRecalCenter = true;
			} else if (mViewStartY > mMaxViewStartY) {
				mViewStartY = mMaxViewStartY;
				mRecalCenter = true;
			}
			if (mRecalCenter) {
				// Calculate the hour that correspond to the average of the Y
				// touch points
				mGestureCenter = (mViewStartY + focusY) / mcellHeight;
				mRecalCenter = false;
			}
			computeFirstMonth();
			// }
			mScrolling = true;

			mSelectionMode = SELECTION_HIDDEN;
			invalidate();
			return true;

		}

		private void computeScrollSurfaceSize(Point out) {
			out.set((int) (mYearRect.width() * (AXIS_X_MAX - AXIS_X_MIN) / mCurrentViewRect
					.width()), (int) (mYearRect.height()
					* (AXIS_Y_MAX - AXIS_Y_MIN) / mYearRect.height()));
		}

		private void setViewMonthAfterScroll(float y) {

			float curHeight = mYearRect.height();
			// x = Math.max(AXIS_X_MIN, Math.min(x, AXIS_X_MAX - curWidth));
			// y = Math.max(AXIS_Y_MIN + curHeight, Math.min(y, AXIS_Y_MAX));

			mCurrentViewRect.set(mCurrentViewRect.left,
					(int) mCurrentViewRect.top - y, mCurrentViewRect.width(),
					(int) y + mCurrentViewRect.bottom);
			// ViewCompat.postInvalidateOnAnimation(this);
		}

		// calculate the average between many counter
		private float getAverageY(MotionEvent me) {
			int count = me.getPointerCount();
			float focusY = 0;
			for (int i = 0; i < count; i++) {
				focusY += me.getY(i);
			}
			focusY /= count;
			return focusY;
		}
	}

}
